using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEditor;
using UnityEditor.U2D;
using UnityEditor.U2D.Sprites;
using UnityEngine;
using UnityEngine.U2D;
using Debug = UnityEngine.Debug;

namespace BearUtil
{
    /// <summary>
    /// TexturePacker 命令行工具，因为美术 命名不够规范，文件夹层级也不够，采用 PlanB
    /// </summary>
    [InlineEditor, ExecuteInEditMode]
    public class TPCommandTool : BearScriptableObject
    {
        [FolderPath] public string OutputPath;
        [FolderPath] public string SearchPath;
        [Sirenix.OdinInspector.FilePath] public string TPSSetting;
        private string resPath = "sprite";

        #region Init

        /// <summary>
        /// 固定生成路径
        /// </summary>
        public override string DefaultPath
        {
            get => "Assets/TPCommand.asset";
        }

        /// <summary>
        /// 单例对象，快速获取
        /// </summary>
        private static TPCommandTool _instance;

        public static TPCommandTool Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = Init();
                }

                return _instance;
            }
        }

        // 用于Tools 手动创建对象，前期测试使用
        [MenuItem("Tools/BearTool/TP Command")]
        private static void menuOption()
        {
            generate<TPCommandTool>();
        }

        /// <summary>
        /// 初始化，新工具可以按照这个模板来重新写
        /// </summary>
        /// <returns></returns>
        public static TPCommandTool Init()
        {
            var instance = ScriptableObject.CreateInstance<TPCommandTool>();
            instance = AssetDatabase.LoadAssetAtPath<TPCommandTool>(instance.DefaultPath);
            if (instance == null)
            {
                TPCommandTool.menuOption();
                instance = AssetDatabase.LoadAssetAtPath<TPCommandTool>(instance.DefaultPath);
            }

            TPCommandTool._instance = instance;

            return instance;
        }

        #endregion

        // 相关文件地址信息
        [HideInInspector] public List<PathData> pathDatas = new List<PathData>();
        [HideInInspector] public List<TPData> tpDatas = new List<TPData>();


        /// <summary>
        /// PlanB，目标地址
        /// </summary>
        [LabelText("检索路径"), FolderPath] public string targetPath;

        [LabelText("最大顶点数")] public int MaxPointCount = 30;
        [LabelText("顶点异常对象"), TableList] public List<TPData> overPointsTargets = new List<TPData>();

        #region Generate TP Data

        private Process cmd;

        /// <summary>
        /// 自动生成 sprite 顶点数据
        /// </summary>
        // [Button("Auto generate outline vertex data", ButtonSizes.Large)]
        private void generateOutlineVertex()
        {
            initCmd();

            if (pathDatas == null)
                pathDatas = new List<PathData>();
            pathDatas.Clear();

            var tP = Application.dataPath + "/" + SearchPath;
            var folders = Directory.GetDirectories(tP, resPath, SearchOption.AllDirectories);

            // -------------------------------------------------
            for (int i = 0; i < folders.Length; i++)
            {
                var folder = folders[i].Replace(@"\", @"/");
                // mapName
                var parentFolder = Path.GetFileName(Path.GetDirectoryName(folder));
                // spriteName
                var spfolders = Directory.GetDirectories(folder, "**", SearchOption.TopDirectoryOnly);

                // create folder
                var destRelaPath = $"{OutputPath}/{parentFolder}";
                var destPath = Application.dataPath.Replace("Assets", destRelaPath);

                if (!Directory.Exists(destPath))
                    Directory.CreateDirectory(destPath);
                else
                {
                    new DirectoryInfo(destPath).GetFiles().ForEach(file => file.Delete());
                }

                for (int j = 0; j < spfolders.Length; j++)
                {
                    var pd = new PathData();
                    pd.ABS = destPath;
                    pd.EXT = ".tpsheet";
                    pd.ParentFolderName = parentFolder;
                    var spfolder = spfolders[j].Replace(@"\", @"/");
                    pd.FolderName = Path.GetFileName(spfolder);
                    execute(spfolder, $"{destRelaPath}/{pd.FolderName}" + "{n1}" + pd.EXT);

                    pathDatas.Add(pd);
                }
            }

            closeCmd();

            ParseDatas();
        }

        /// <summary>
        /// 指定路径生成 tpsheet，并且替换
        /// </summary>
        [Button("Generate vertices with targetPath", ButtonSizes.Large)]
        private void generateOutlineVertex2()
        {
            if (string.IsNullOrEmpty(SearchPath))
                return;

            if (tpDatas == null)
                tpDatas = new List<TPData>();
            tpDatas.Clear();

            if (overPointsTargets == null)
                overPointsTargets = new List<TPData>();
            overPointsTargets.Clear();

            // create folder
            var destRelaPath = $"{OutputPath}/Tmp";
            var destPath = Application.dataPath.Replace("Assets", destRelaPath);

            if (!Directory.Exists(destPath))
                Directory.CreateDirectory(destPath);
            else
            {
                new DirectoryInfo(destPath).GetFiles().ForEach(file => file.Delete());
            }

            initCmd();

            var pngs = Directory.GetFiles(Application.dataPath.Replace("Assets", targetPath), "**.png",
                SearchOption.AllDirectories);
            for (int i = 0; i < pngs.Length; i++)
            {
                var png = pngs[i];
                var relaPath = png.Replace(@"\", @"/").Replace(Application.dataPath, "Assets");
                var output = destRelaPath + $"/{Path.GetFileNameWithoutExtension(png)}.tpsheet";

                // generate tpsheet
                execute(png, output);

                var sheetFile = Application.dataPath.Replace("Assets", output);
                using (StreamReader sr = new StreamReader(sheetFile))
                {
                    int line = 0;
                    string rawLine;
                    while (!string.IsNullOrEmpty(rawLine = sr.ReadLine()) || line < MinLine)
                    {
                        if (line++ < MinLine)
                            continue;

                        // parse row data 
                        var tData = parseRawData(rawLine);
                        tData.AssetPath = relaPath;
                        tpDatas.Add(tData);

                        if (tData != null && tData.vertices.Length > MaxPointCount)
                            overPointsTargets.Add(tData);

                        try
                        {
                            // 重新导入数据
                            if (tData != null)
                            {
                                var importer = AssetImporter.GetAtPath(relaPath);
                                var sp = AssetDatabase.LoadAssetAtPath<Sprite>(relaPath);
                                var mainPath = AssetDatabase.LoadMainAssetAtPath(relaPath);
                                
                                var factory = new SpriteDataProviderFactories();
                                factory.Init();
                                var provider = factory.GetSpriteEditorDataProviderFromObject(mainPath);
                                // now we try the importer
                                // provider = AssetImporter.GetAtPath(tData.AssetPath) as ISpriteEditorDataProvider;
                                if (provider == null)
                                    return;
                                
                                provider.InitSpriteEditorDataProvider();
                                
                                var offsetVertices = new Vector2[tData.vertices.Length];
                                for (int j = 0; j < tData.vertices.Length; j++)
                                {
                                    offsetVertices[j] = tData.vertices[j];
                                    offsetVertices[j].x -= tData.size.x * tData.anchor.x;
                                    offsetVertices[j].y -= tData.size.y * tData.anchor.y;
                                }
                                
                                var outlines = new List<Vector2[]>() {offsetVertices};
                                
                                provider.GetDataProvider<ISpriteOutlineDataProvider>()
                                    .SetOutlines(new GUID(AssetDatabase.AssetPathToGUID(relaPath)), outlines);
                                provider.Apply();
                                
                                importer.SaveAndReimport();
                            }
                        }
                        catch (Exception e)
                        {
                            Debug.LogError(e);
                            Debug.LogError(tData.name);
                        }
                    }
                }
            }

            AssetDatabase.Refresh();

            closeCmd();
        }

        private void initCmd()
        {
            cmd = new Process();
            cmd.StartInfo.FileName = "D:\\TexturePacker\\bin\\TexturePacker.exe";

            cmd.StartInfo.CreateNoWindow = true;
            cmd.StartInfo.UseShellExecute = false;
            cmd.StartInfo.RedirectStandardInput = true; // 重定向输入     
            cmd.StartInfo.RedirectStandardOutput = true; // 重定向标准输出    
            cmd.StartInfo.StandardOutputEncoding = Encoding.UTF8;
        }

        private void execute(string spritesPath, string outputPath)
        {
            if (cmd == null)
                return;

            cmd.StartInfo.Arguments = $"{TPSSetting} {spritesPath} --data {outputPath} --multipack";
            cmd.Start();
            cmd.WaitForExit();
        }

        private void closeCmd()
        {
            if (cmd == null)
                return;
            cmd.Close();
        }

        #endregion

        #region Convert TP vertices to unity

        // 数据从第 16 行开始
        [NonSerialized] private int MinLine = 15;

        /// <summary>
        ///  尝试 1-5 atlas
        /// </summary>
        [NonSerialized] private int MaxTest = 10;

        // [Button("测试(直接替换数据)", ButtonSizes.Large)]
        public void ParseDatas()
        {
            if (tpDatas == null)
                tpDatas = new List<TPData>();
            tpDatas.Clear();

            if (overPointsTargets == null)
                overPointsTargets = new List<TPData>();
            overPointsTargets.Clear();

            // load file --------------------------------------------------
            for (int i = 27; i < 28; i++)
            {
                var pd = pathDatas[i];
                for (int k = 1; k < MaxTest; k++)
                {
                    var relaPath = $"{OutputPath}/{pd.ParentFolderName}/{pd.FolderName}{k}{pd.EXT}";
                    var realPath = Application.dataPath.Replace("Assets", relaPath);
                    if (!File.Exists(realPath))
                        break;

                    Debug.LogError(realPath);
                    pd.FileAssetPath = $"{SearchPath}/{pd.ParentFolderName}/{resPath}/{pd.FolderName}";

                    using (StreamReader sr = new StreamReader(realPath))
                    {
                        int line = 0;
                        string rawLine;
                        while (!string.IsNullOrEmpty(rawLine = sr.ReadLine()) || line < MinLine)
                        {
                            if (line++ < MinLine)
                                continue;

                            // parse row data 
                            var tData = parseRawData(rawLine);
                            tpDatas.Add(tData);

                            if (tData != null && tData.vertices.Length > MaxPointCount)
                                overPointsTargets.Add(tData);

                            try
                            {
                                if (tData != null)
                                    AssetImporter.GetAtPath($"Assets/{pd.FileAssetPath}/{tData.name}.png")
                                        .SaveAndReimport();
                            }
                            catch (Exception e)
                            {
                                Debug.LogError(e);
                                Debug.LogError(tData.name);
                            }
                        }
                    }
                }
            }
        }

        // 分析数据
        private TPData parseRawData(string content)
        {
            TPData data = new TPData();
            try
            {
                var ps = content.Split(new string[] {"; "}, StringSplitOptions.None);

                // p1 data
                var p1 = ps[0].Split(';');
                data.name = p1[0];
                data.offset = new Vector2Int(Convert.ToInt16(p1[1]), Convert.ToInt16(p1[2]));
                data.size = new Vector2Int(Convert.ToInt16(p1[3]), Convert.ToInt16(p1[4]));

                // anchor
                var p2 = ps[1].Split(';');
                data.anchor = new Vector2(Convert.ToSingle(p2[0]), Convert.ToSingle(p2[1]));

                // pivot
                var p3 = ps[2].Split(';');
                data.padding = new Vector4(Convert.ToInt16(p3[0]), Convert.ToInt16(p3[1]), Convert.ToInt16(p3[2]),
                    Convert.ToInt16(p3[3]));

                // vertices
                var p4 = ps[3].Split(';');
                int count = Convert.ToInt16(p4[0]);
                data.vertices = new Vector2[count];
                for (int i = 0; i < count; i++)
                {
                    data.vertices[i] = new Vector2(Convert.ToInt16(p4[1 + 2 * i]), Convert.ToInt16(p4[2 + 2 * i]));
                }

                // indices
                var p5 = ps[4].Split(';');
                data.indices = new ushort[p5.Length - 1];
                for (int i = 1; i < p5.Length; i++)
                {
                    data.indices[i - 1] = Convert.ToUInt16(p5[i]);
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                Debug.LogError(data.name);
            }

            return data;
        }

        #endregion
    }

    public class SpriteImport : AssetPostprocessor
    {
        private void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
        {
            try
            {
                for (int i = 0; i < sprites.Length; i++)
                {
                    var sp = sprites[i];
                    var tData = TPCommandTool.Instance.tpDatas.Find(data => data.name.Equals(sp.name));
                    if (tData == null)
                        return;
                    sp.OverrideGeometry(tData.vertices, tData.indices);
                }
            }
            catch (Exception e)
            {
            }
        }
    }

    /// <summary>
    /// TextPacker Data
    /// </summary>
    public class TPData
    {
        public string name;
        public Vector2Int size;
        public Vector2Int offset;
        public Vector2 anchor;
        public Vector4 padding;

        public Vector2[] vertices;
        public ushort[] indices;
        public string AssetPath;
    }

    /// <summary>
    /// 记录 path 数据
    /// </summary>
    [SerializeField]
    public class PathData
    {
        public string ABS;
        public string RELA;
        public string EXT;

        public string FileAssetPath;
        public string FileName;

        public string FolderName;

        // 父节点某一个文件夹名称
        public string ParentFolderName;
    }
}